package com.api.client;


import java.util.ArrayList;
import java.util.List;
import java.lang.reflect.Type;
import java.util.Map;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.api.client.json.ApiResultType;
import com.app.AppContext;
import com.google.gson.Gson;

public class ApiClient {

	private static final String serviceLocation = "classpath:/api-service.xml";
	
	private static final RequestFactory requestFactory = AppContext.getBean(RequestFactory.class);
	
	private static final ApiProvider provider = AppContext.getBean(ApiProvider.class);
	
	private static final ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { serviceLocation });
	
	private AccessKeys accessKeys;
	
	public ApiClient(AccessKeys accessKeys){
		
		this.accessKeys = accessKeys;
	}
	
	public ApiClient(String email, String password){
		
		this.accessKeys = provider.getAccessKeys(email, password);
	}
	
	public <T> T callService(Object serviceId, Object... values){
		
		ServiceRequest request = newRequest(serviceId);
		
		Service service = request.getService();
		VariablePath path = service.getPath();
		
		List<String> vars = new ArrayList<String>();
		vars.addAll(path.getVariables());
		vars.addAll(service.getRequiredParameters());
		
		int i = 0;
		
		while(i < vars.size() && i < values.length){
			
			request.setParameter(vars.get(i), values[i]);
			
			i++;
		}
		
		return request.send();
	}
	
	@SuppressWarnings("unchecked")
	private <T> T sendSigned(Service service, Request request) {

		Response response = request.sendSigned(accessKeys.getKey(), accessKeys.getSecret());
		
		T returnResult = null;
		
		if(response.getStatus() == ResponseStatus.OK){
		
			ApiResultType type = service.getResultType();
			
			if(type != ApiResultType.URN){
			
				T result = (T) parseApiResult(response.getBody(), type.getType());
				returnResult = result;
			}
			else{
				
				T result = (T) response.getBody();
				returnResult = result;
			}
			
			return returnResult;
		}
		else
			throw new IllegalStateException(String.format("Service response error '%s'", response.getStatus()));

	}

	public ServiceRequest newRequest(Object serviceId){
		
		return new ServiceRequestImp(this, newService(serviceId));
	}
	
	<T> T send(Service service, Map<String, Object> pathParams, Map<String, Object> params) {

		Request request = requestFactory.newRequest(service, pathParams, params);
	
		return sendSigned(service, request);
	}
	
	public static Service newService(Object serviceId){
		
		String id = String.valueOf(serviceId);
		return (Service) context.getBean(id);
	}
	
	private static <T> T parseApiResult(String json, Type type){
		
		Gson gson = new Gson();
		T result = gson.fromJson(json, type);
	
		return result;
	}
	
	public static ApiClient forAccess(String accessKey, String accessSecret){
		
		AccessKeys accessKeys = new AccessKeys(accessKey, accessSecret);
		
		return new ApiClient(accessKeys);
	}
	
	public static ApiClient forUser(String email, String password){
		
		AccessKeys accessKeys = provider.getAccessKeys(email, password);
		
		return new ApiClient(accessKeys);
	}
}
